package com.ledict.casefiling.config.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * shiro之密码输入次数限制6次，并锁定2分钟
 */
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
    //集群中可能会导致出现验证多过5次的现象，因为AtomicInteger只能保证单节点并发
    //解决方案，利用ehcache、redis（记录错误次数）和mysql数据库（锁定）的方式处理：密码输错次数限制； 或两者结合使用
    private Cache<String, AtomicInteger> passwdRetryCache;

    public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
        //读取ehcache中配置的登录限制锁定时间
        passwdRetryCache = cacheManager.getCache("passwordRetryCache");
    }

    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        //获取登录用户名
        String curUserName = (String) token.getPrincipal();

        //从ehcache中获取密码输错次数
        AtomicInteger retryCount = passwdRetryCache.get(curUserName);

        if (null == retryCount) {
            //第一次
            retryCount = new AtomicInteger(0);
            passwdRetryCache.put(curUserName, retryCount);
        }

        if (retryCount.incrementAndGet() > 5) {
            // if retry count > 5 throw  超过5次 锁定
            throw new ExcessiveAttemptsException("username:" + curUserName + "tried to login more than 5 times in period");
        }

        //否则走判断密码逻辑
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
            // clear retry count  清楚ehcache中的count次数缓存
            passwdRetryCache.remove(curUserName);
        }

        return matches;
    }
}
